package com.tempjob.admin.aspect;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.tempjob.admin.security.annotation.AdminLogIgore;
import com.tempjob.common.context.AdminContextManage;
import com.tempjob.common.context.AdminContext;
import com.tempjob.common.exception.BusinessException;
import com.tempjob.common.exception.EsExecuteException;
import com.tempjob.common.exception.ParamException;
import com.tempjob.common.model.BaseRequest;
import com.tempjob.common.model.ResponseCode;
import com.tempjob.common.model.ResponseData;
import com.tempjob.common.utils.HttpContextUtils;
import com.tempjob.common.utils.IPUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;

import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.groups.Default;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

@Slf4j
@Aspect
@Component
@Order(1)
public class ControllerAspect {

    @Resource
    private LocalValidatorFactoryBean localValidatorFactoryBean;

    @Pointcut("execution(* com.tempjob.admin.controller.*.*(..)) || execution(* com.tempjob.admin.controller.*.*(..))")
    public void validationPointCut() {
    }

    @Around(value = "validationPointCut()")
    public Object aroundExecute(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = null;
        ResponseData response = null;
        //获取request
        HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
        String ip = IPUtils.getIpAddr(request);
        //请求的参数
        Map<String, Object> argSb = new HashMap<>();
        Object[] argg = joinPoint.getArgs();
        if (!ServletFileUpload.isMultipartContent(request)) {
            for (Object arg : argg) {
                if (arg instanceof ServletRequest) {
                    HttpServletRequest httpRequest = (HttpServletRequest) arg;
                    argSb.put("requestparameters", httpRequest.getParameterMap());
                    continue;
                }
                if (null == arg || arg instanceof ServletResponse || arg instanceof BindingResult
                        || arg.getClass().getName().contains("TraceHttpServletResponse")) {
                    continue;
                }
                argSb.put(arg.getClass().getSimpleName(), arg);
            }
        }
        try {
            result = joinPoint.proceed();
        } catch (ParamException e) {
            log.warn(e.getMessage(), e);
            result = new ResponseData(ResponseCode.PARAM_EXCEPTION, "参数错误，请检查方法参数:" + e.getMessage());
        } catch (BusinessException e) {
            log.warn(e.getMessage(), e);
            result = new ResponseData(ResponseCode.BUSINESS_EXCEPTION, e.getMessage());
        } catch (EsExecuteException e) {
            result = new ResponseData(ResponseCode.ES_EXCEPTION, e.getMessage());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            result = new ResponseData(ResponseCode.SYSTEM_EXCEPTION, "系统繁忙，请稍后再试");
        } catch (Throwable e) {
            log.error(e.getMessage(), e);
            result = new ResponseData(ResponseCode.SYSTEM_EXCEPTION, "系统繁忙，请稍后再试");
        }
        if (result instanceof ResponseData) {
            response = (ResponseData) result;
        } else {
            response = new ResponseData();
            response.setData(result);
        }
        Class<?> targetCls = joinPoint.getTarget().getClass();
        //获取方法签名(通过此签名获取目标方法信息)
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        //获取目标方法上的注解指定的操作名称
        Method targetMethod = targetCls.getDeclaredMethod(ms.getName(), ms.getParameterTypes());
        AdminLogIgore adminLogIgore = targetMethod.getAnnotation(AdminLogIgore.class);
        if (adminLogIgore == null) {
            AdminContext userInfo = AdminContextManage.getContext();
            log.info("ip:{}，接口地址{}，请求方式{}，入参：{},出参:{},uid：{},耗时:{}", ip, request.getRequestURL(), request.getMethod(), JSON.toJSONString(argSb, SerializerFeature.IgnoreNonFieldGetter), JSON.toJSONString(response, SerializerFeature.IgnoreNonFieldGetter), userInfo == null ? null : userInfo.getId(), System.currentTimeMillis() - startTime);
        }
        return response;
    }

    @Before("validationPointCut() && args(request,..)")
    public void validateParameter(JoinPoint joinPoint, BaseRequest request) throws ParamException {
        Set<ConstraintViolation<BaseRequest>> validErrors = this.localValidatorFactoryBean.validate(request, new Class[]{Default.class});
        Iterator iterator = validErrors.iterator();
        StringBuilder errorMsg = new StringBuilder();
        while (iterator.hasNext()) {
            ConstraintViolation constraintViolation = (ConstraintViolation) iterator.next();
            String error = constraintViolation.getPropertyPath() + ":" + constraintViolation.getMessage();
            errorMsg.append(iterator.hasNext() ? error + "; " : error);
        }
        if (!validErrors.isEmpty()) {
            throw new ParamException(errorMsg.toString());
        }
    }


}
